package repository

import (
	"context"
	"time"

	"github.com/devops-smartbot/devops-smartbot/services/notification-service/internal/models"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type NotificationRepository struct {
	notificationCol  *mongo.Collection
	preferencesCol   *mongo.Collection
}

func NewNotificationRepository(db *mongo.Database) *NotificationRepository {
	return &NotificationRepository{
		notificationCol: db.Collection("notifications"),
		preferencesCol:  db.Collection("user_preferences"),
	}
}

// CreateNotification creates a new notification record
func (r *NotificationRepository) CreateNotification(ctx context.Context, notification *models.Notification) error {
	notification.CreatedAt = time.Now()
	notification.Status = models.StatusPending
	notification.RetryCount = 0

	_, err := r.notificationCol.InsertOne(ctx, notification)
	return err
}

// UpdateNotificationStatus updates notification status
func (r *NotificationRepository) UpdateNotificationStatus(ctx context.Context, id string, status models.Status, errorMsg string) error {
	update := bson.M{
		"$set": bson.M{
			"status": status,
		},
	}

	if status == models.StatusSent {
		now := time.Now()
		update["$set"].(bson.M)["sentAt"] = now
	}

	if errorMsg != "" {
		update["$set"].(bson.M)["errorMessage"] = errorMsg
	}

	if status == models.StatusRetrying {
		update["$inc"] = bson.M{"retryCount": 1}
	}

	_, err := r.notificationCol.UpdateOne(
		ctx,
		bson.M{"_id": id},
		update,
	)
	return err
}

// GetNotificationByID retrieves a notification by ID
func (r *NotificationRepository) GetNotificationByID(ctx context.Context, id string) (*models.Notification, error) {
	var notification models.Notification
	err := r.notificationCol.FindOne(ctx, bson.M{"_id": id}).Decode(&notification)
	if err != nil {
		return nil, err
	}
	return &notification, nil
}

// GetNotifications retrieves notifications with filters
func (r *NotificationRepository) GetNotifications(ctx context.Context, filters bson.M, page, pageSize int) ([]*models.Notification, int, error) {
	// Count total
	total, err := r.notificationCol.CountDocuments(ctx, filters)
	if err != nil {
		return nil, 0, err
	}

	// Find with pagination
	skip := int64((page - 1) * pageSize)
	limit := int64(pageSize)

	opts := options.Find().
		SetSkip(skip).
		SetLimit(limit).
		SetSort(bson.D{{Key: "createdAt", Value: -1}})

	cursor, err := r.notificationCol.Find(ctx, filters, opts)
	if err != nil {
		return nil, 0, err
	}
	defer cursor.Close(ctx)

	var notifications []*models.Notification
	if err := cursor.All(ctx, &notifications); err != nil {
		return nil, 0, err
	}

	return notifications, int(total), nil
}

// GetPendingNotifications retrieves pending notifications for retry
func (r *NotificationRepository) GetPendingNotifications(ctx context.Context, maxRetries int) ([]*models.Notification, error) {
	filters := bson.M{
		"status": bson.M{
			"$in": []models.Status{models.StatusPending, models.StatusRetrying},
		},
		"retryCount": bson.M{"$lt": maxRetries},
	}

	cursor, err := r.notificationCol.Find(ctx, filters)
	if err != nil {
		return nil, err
	}
	defer cursor.Close(ctx)

	var notifications []*models.Notification
	if err := cursor.All(ctx, &notifications); err != nil {
		return nil, err
	}

	return notifications, nil
}

// GetUserPreferences retrieves user notification preferences
func (r *NotificationRepository) GetUserPreferences(ctx context.Context, userID string) (*models.UserPreferences, error) {
	var prefs models.UserPreferences
	err := r.preferencesCol.FindOne(ctx, bson.M{"userId": userID}).Decode(&prefs)
	if err == mongo.ErrNoDocuments {
		// Return default preferences
		return &models.UserPreferences{
			UserID:             userID,
			EmailEnabled:       true,
			SlackEnabled:       true,
			DingTalkEnabled:    false,
			EventSubscriptions: make(map[string]bool),
			UpdatedAt:          time.Now(),
		}, nil
	}
	if err != nil {
		return nil, err
	}
	return &prefs, nil
}

// UpdateUserPreferences updates user notification preferences
func (r *NotificationRepository) UpdateUserPreferences(ctx context.Context, prefs *models.UserPreferences) error {
	prefs.UpdatedAt = time.Now()

	upsert := true
	opts := &options.UpdateOptions{
		Upsert: &upsert,
	}

	_, err := r.preferencesCol.UpdateOne(
		ctx,
		bson.M{"userId": prefs.UserID},
		bson.M{"$set": prefs},
		opts,
	)
	return err
}
